#include "udsClient.h"

#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <algorithm>
#include <thread>

#include <sys/un.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "udsCmdDef.h"

bool udsClient::shortRequest(const char *path, int cmd, void *msg, int msgLen, udsFunc func, void *obj)
{
    int fd = -1; 
    udsCommon::tryGetConnectedUdsFd(fd, path);

    return shortRequestByFd(fd, cmd, msg, msgLen, func, obj);
}

bool udsClient::shortRequest(const char *ip, int port, int cmd, void *msg, int msgLen, udsFunc func, void *obj)
{
    int fd = -1; 
    udsCommon::tryGetConnectedTcpFd(fd, ip, port);
	
    return shortRequestByFd(fd, cmd, msg, msgLen, func, obj);
}

bool udsClient::shortRequestByFd(int fd, int cmd, void *msg, int msgLen, udsFunc func, void *obj)
{

    if (fd < 0)
        return false;

    if ( msgLen == udsCommon::sendMsg(fd, cmd, msg, msgLen) )
    {
        if ( func == NULL )
        {
            return true;
        }

        p_UdsHandler pHandler = udsCommon::creatHandler();
        pHandler->pObj = obj;
        pHandler->fd = fd;
        if (udsCommon::getUdsPacks(pHandler, 2000) > 0)
        {
            func(pHandler);
        }
        udsCommon::destroyHandler(pHandler);

        return true;
    }
    return false;
}

int udsClient::connectedTcp(const char *ip, int port, udsFunc func, void *pObj)
{
    int fd = -1;
    udsCommon::tryGetConnectedTcpFd(fd, ip, port);
    if (fd > 0)
    {
        _pushPollFd(fd);
        p_FuncHandler pFunc = new FuncHandler();
        pFunc->func = func;
         pFunc->stdfunc = NULL;
        pFunc->pObj = pObj;
        udsFuncMap_[fd] = pFunc;
    }
        
    return fd;
}

int udsClient::connectedUds(const char *paths, udsFunc func, void *pObj)
{
    int fd = -1;
    udsCommon::tryGetConnectedUdsFd(fd, paths);
    if (fd > 0)
    {
        _pushPollFd(fd);
        p_FuncHandler pFunc = new FuncHandler();
        pFunc->func = func;
        pFunc->stdfunc = NULL;
        pFunc->pStdFunc = NULL;
        pFunc->pObj = pObj;
        udsFuncMap_[fd] = pFunc;
    }
    return fd;
}

int udsClient::connectedTcp_v1(const char *ip, int port, stdUdsFunc func, void *pObj)
{
    int fd = -1;
    udsCommon::tryGetConnectedTcpFd(fd, ip, port);
    if (fd > 0)
    {
        _pushPollFd(fd);
        p_FuncHandler pFunc = new FuncHandler();
        pFunc->func = NULL;
        pFunc->stdfunc = func;
        pFunc->pStdFunc = NULL;
        pFunc->pObj = pObj;
        udsFuncMap_[fd] = pFunc;
    }
        
    return fd;
}

int udsClient::connectedTcp_v2(const char *ip, int port, stdUdsFunc *func, void *pObj)
{
    int fd = -1;
    udsCommon::tryGetConnectedTcpFd(fd, ip, port);
    if (fd > 0)
    {
         _pushPollFd(fd);
        p_FuncHandler pFunc = new FuncHandler();
        pFunc->func = NULL;
        pFunc->stdfunc = NULL;
        pFunc->pStdFunc = func;
        pFunc->pObj = pObj;
        udsFuncMap_[fd] = pFunc;
    }
        
    return fd;
}

int udsClient::connectedUds_v1(const char *paths, stdUdsFunc func, void *pObj)
{
    int fd = -1;
    udsCommon::tryGetConnectedUdsFd(fd, paths);

    if (fd > 0)
    {
        _pushPollFd(fd);
        p_FuncHandler pFunc = new FuncHandler();
        pFunc->func = NULL;
        pFunc->stdfunc = func;
        pFunc->pStdFunc = NULL;
        pFunc->pObj = pObj;
        udsFuncMap_[fd] = pFunc;
    }
    return fd;
}


int udsClient::connectedUds_v2(const char *paths, stdUdsFunc *func, void *pObj)
{
    int fd = -1;
    udsCommon::tryGetConnectedUdsFd(fd, paths);

    if (fd > 0)
    {
        _pushPollFd(fd);
        p_FuncHandler pFunc = new FuncHandler();
        pFunc->func = NULL;
        pFunc->stdfunc = NULL;
        pFunc->pStdFunc = func;
        pFunc->pObj = pObj;
        udsFuncMap_[fd] = pFunc;
    }
    return fd;
}

void udsClient::stopClient()
{
    disConnectAll();
    udsClientInit_ = false;
    udsClientThread_.close();
}

void udsClient::disConnectFd(int fd)
{
    _removePollFd(fd);
    if(udsFuncMap_[fd])
    {
        udsFuncMap_[fd]->func = NULL;
        udsFuncMap_[fd]->stdfunc = NULL;
        udsFuncMap_[fd]->pStdFunc = NULL;
        udsFuncMap_[fd]->pObj = NULL;
        delete udsFuncMap_[fd];
        udsFuncMap_[fd] = NULL;
    }
    close(fd);
}

void udsClient::disConnectAll()
{
    auto udsFunc = udsFuncMap_.begin();
    while(udsFunc != udsFuncMap_.end())
    {
        if(udsFunc->second)
        {
            disConnectFd(udsFunc->first);
        }
        udsFunc++;
    }
}

int udsClient::sendMsg(int fd, int cmd, void *msg, int msgLen)
{
    return udsCommon::sendMsg(fd, cmd, msg, msgLen);
}

int udsClient::sendPacks(int fd, int cmd, void **frame_addr, size_t *frame_size, int pack_count)
{
    return udsCommon::sendPacks(fd, cmd, frame_addr, frame_size, pack_count);
}

bool udsClient::startClient()
{
    if (udsClientInit_)
        return false;

    udsClientThread_.start("[udsClient::udsClientThread_]", this, &udsClient::udsPollDisponse);
    udsClientInit_ = true;

    return true;
}

bool udsClient::startClient(const char * thread_name)
{
    if (udsClientInit_)
        return false;

    udsClientThread_.start(thread_name, this, &udsClient::udsPollDisponse);
    udsClientInit_ = true;

    return true;
}

void udsClient::udsPollDisponse()
{
    /* int num = poll(&pollFdVector_[0], pollFdVector_.size() + 1, 500); */
    int num = poll(&pollFdVector_[0], pollFdVector_.size(), 10);
    if ( num < 0 )
    {
        fprintf(stderr, "poll error, code = %d\n", errno);
        return;
    }
    else if (num == 0)
    {
        return;
    }
        
    for ( auto &por : pollFdVector_ ) 
    {
        if ( !por.revents) 
        {
            continue;
        }
        por.revents = 0;

        p_UdsHandler pCmd = udsCommon::creatHandler();
        pCmd->fd = por.fd;
        if (  udsCommon::getUdsPacks(pCmd, 2000) > 0 )
        {
            if ( pCmd->cmd > 0)
            {
                p_FuncHandler pHandler = udsFuncMap_[pCmd->fd];
                if ( pHandler )
                {
                    pCmd->pObj = pHandler->pObj;
                    if (pHandler->func)
                    {
                        pHandler->func(pCmd);
                    }
                    else if (pHandler->stdfunc)
                    {
                        pHandler->stdfunc(pCmd);
                    }
                    else if (pHandler->pStdFunc)
                    { 
                        (*(pHandler->pStdFunc))(pCmd);
                    }
                }   
            }
            pCmd->fd = -1; 
        }
        else
        {
            fprintf(stderr, "getUdsPacks error, code = %d\n", errno);
            p_FuncHandler pHandler = udsFuncMap_[pCmd->fd];
            if (pHandler)
            {
                pCmd->cmd = UDS_CMD_SOCKET_ERR;
                pCmd->pObj = pHandler->pObj;
                if (pHandler->func)
                {
                    pHandler->func(pCmd);
                }
                else if (pHandler->stdfunc)
                {
                    pHandler->stdfunc(pCmd);
                }
                else if (pHandler->pStdFunc)
                { 
                    (*(pHandler->pStdFunc))(pCmd);
                }
                // fprintf(stderr, "getUdsPacks error cb end \n");
            }
            disConnectFd(pCmd->fd);
            pCmd->fd = -1;
        }
        udsCommon::destroyHandler(pCmd);
    }
}

void udsClient::_removePollFd(int fd)
{
    std::lock_guard<std::mutex> lock(pollFdVectorMutex_);
	auto newend = remove_if(pollFdVector_.begin(), pollFdVector_.end(), [&fd](pollfd test) {
		return test.fd == fd;
	});
	pollFdVector_.erase(newend, pollFdVector_.end());
}

void udsClient::_pushPollFd(int fd)
{
    std::lock_guard<std::mutex> lock(pollFdVectorMutex_);
    pollfd tmp_poll;
    tmp_poll.fd = fd;
    tmp_poll.events = POLLIN;
    tmp_poll.revents = 0;
    pollFdVector_.emplace_back(tmp_poll);   //emplace_back  和 push 功能一样效率更高  仅 c++11 以后支持  
}


uds_client_base::uds_client_base(const char * uds_path) {
	if (_init) return;

	_instance = new udsClient();
	if (!_instance) return;

	_instance->startClient(uds_path);
	_uds_path = (char *)uds_path;
	_init = true;
}

uds_client_base::~uds_client_base() {
	if (!_instance) return;

    _instance->stopClient();
    delete _instance;
    _instance = NULL;
    _init = false;
}

void uds_client_base::destroy_long_connect(int fd) {
	_instance->disConnectFd(fd);		
	fd = -1;
}

int uds_client_base::create_long_connect(stdUdsFunc  *func, void *pObj, int cmd, void * msg, int msg_len) {
	int fd = -1;
	int ret = 0;

	if (!_uds_path || !_init) return -1;

	fd = _instance->connectedUds_v2(_uds_path, func, pObj);
	if (fd == -1) return -1;

	if (ret = _instance->sendMsg(fd, cmd, msg, msg_len) <= 0) {
		destroy_long_connect(fd);
	}

	return fd;
}


int uds_client_base::create_long_connect(stdUdsFunc  func, void *pObj, int cmd, void * msg, int msg_len) {
	int fd = -1;
	int ret = 0;

	if (!_uds_path || !_init) return -1;

	fd = _instance->connectedUds_v1(_uds_path, func, pObj);
	if (fd == -1) return -1;

	if (ret = _instance->sendMsg(fd, cmd, msg, msg_len) <= 0) {
		destroy_long_connect(fd);
	}

	return fd;
}

int uds_client_base::create_short_connect(int cmd, void * msg, int msg_len) {
	if (!_uds_path || !_init) return -1;

	return udsClient::shortRequest(_uds_path, cmd, msg, msg_len, NULL);
}

int uds_client_base::send_msg_with_fd(int fd, int cmd, void *msg, int msgLen) {
	if (fd == -1) return -1;

    return udsClient::shortRequestByFd(fd, cmd, msg, msgLen, NULL, NULL);
}
